﻿
namespace RayDen.Library.Data.Scene {
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using RayDen.Library.Core.Collections;
    using RayDen.Library.Core.Primitives;
    using RayDen.Library.Entity.Scene;

    /// <summary>
    /// Custom binary serializer for scene data.
    /// </summary>
    public class SceneSerializer {
        public string SceneGeometryFileSignature = "RayDen.Library.GeometryData";

        public void WriteScene( Stream inputStream, SceneGeometryInfo sceneGeometry ) {
            using( var wr = new BinaryWriter(inputStream) ) {
                wr.Write(SceneGeometryFileSignature);
                wr.Write(sceneGeometry.GlobalIndexing);
                wr.Write(sceneGeometry.Vertices.Length);
                wr.Write(sceneGeometry.TexCoords.Length);
                wr.Write(sceneGeometry.Normals.Length);
                wr.Write(sceneGeometry.Geometry.Length);

                //--Vertices

                foreach( var vector in sceneGeometry.Vertices ) {
                    wr.Write(vector.x);
                    wr.Write(vector.y);
                    wr.Write(vector.z);
                }

                foreach( var vector in sceneGeometry.Normals ) {
                    wr.Write(vector.x);
                    wr.Write(vector.y);
                    wr.Write(vector.z);
                }

                foreach( var vector in sceneGeometry.TexCoords ) {
                    wr.Write(vector.x);
                    wr.Write(vector.y);
                    wr.Write(vector.z);
                }


                foreach( var geometryInfo in sceneGeometry.Geometry ) {
                    wr.Write(geometryInfo.Name);
                    wr.Write(geometryInfo.MaterialName);
                    wr.Write(geometryInfo.UseNormals);
                    wr.Write(geometryInfo.UseTextures);


                    var indexes = geometryInfo.IndexData.SelectMany(BitConverter.GetBytes).ToArray();
                    var texIndexes = geometryInfo.TextureIndexData.SelectMany(BitConverter.GetBytes).ToArray();
                    var normIndexes = geometryInfo.NormalIndexData.SelectMany(BitConverter.GetBytes).ToArray();

                    wr.Write(geometryInfo.IndexData.Count);
                    wr.Write(geometryInfo.TextureIndexData.Count);
                    wr.Write(geometryInfo.NormalIndexData.Count);

                    wr.Write(indexes);
                    wr.Write(texIndexes);
                    wr.Write(normIndexes);

                }
            }
        }



        public SceneGeometryInfo ReadScene( Stream inputStream ) {
            var sceneGeometry =  new SceneGeometryInfo();

            using( var reader = new BinaryReader(inputStream) ) {
                var sign = reader.ReadString();
                if( !sign.Equals(SceneGeometryFileSignature) ) {
                    throw new ApplicationException("Invalid data format. Signature not found");
                }
                sceneGeometry.GlobalIndexing = reader.ReadBoolean();
                sceneGeometry.Vertices = new Point[reader.ReadInt32()];
                sceneGeometry.TexCoords= new Vector[reader.ReadInt32()];
                sceneGeometry.Normals= new Vector[reader.ReadInt32()];
                sceneGeometry.Geometry= new GeometryInfo[reader.ReadInt32()];

                //--Vertices

                for( int index = 0; index<sceneGeometry.Vertices.Length; index++ ) {
                    sceneGeometry.Vertices[index].x = reader.ReadSingle();
                    sceneGeometry.Vertices[index].y = reader.ReadSingle();
                    sceneGeometry.Vertices[index].z = reader.ReadSingle();
                }

                for( int index = 0; index<sceneGeometry.Normals.Length; index++ ) {
                    sceneGeometry.Normals[index].x = reader.ReadSingle();
                    sceneGeometry.Normals[index].y = reader.ReadSingle();
                    sceneGeometry.Normals[index].z = reader.ReadSingle();
                }

                for( int index = 0; index<sceneGeometry.TexCoords.Length; index++ ) {
                    sceneGeometry.TexCoords[index].x = reader.ReadSingle();
                    sceneGeometry.TexCoords[index].y = reader.ReadSingle();
                    sceneGeometry.TexCoords[index].z = reader.ReadSingle();
                }

                foreach( var geometryInfo in sceneGeometry.Geometry ) {
                    geometryInfo.Name           = reader.ReadString();
                    geometryInfo.MaterialName   = reader.ReadString();
                    geometryInfo.UseNormals     = reader.ReadBoolean();
                    geometryInfo.UseTextures    = reader.ReadBoolean();

                    int indexCount = reader.ReadInt32();
                    int texIndexCount = reader.ReadInt32();
                    int normIndexCount = reader.ReadInt32();

                    var indexData = new List<int>(indexCount);
                    var texIndexData = new List<int>(indexCount);
                    var normIndexData = new List<int>(indexCount);



                    var r = reader.ReadBytes(indexCount*4);
                    for( int i=0; i<indexCount; i=+4 ) {
                        indexData.Add(BitConverter.ToInt32(r, i));
                    }

                    r = reader.ReadBytes(texIndexCount*4);
                    for( int i=0; i<texIndexCount; i=+4 ) {
                        texIndexData.Add(BitConverter.ToInt32(r, i));
                    }

                    r = reader.ReadBytes(normIndexCount*4);
                    for( int i=0; i<normIndexCount; i=+4 ) {
                        normIndexData.Add(BitConverter.ToInt32(r, i));
                    }

                    geometryInfo.IndexData = new IntStore(indexData);
                    geometryInfo.TextureIndexData = new IntStore(texIndexData);
                    geometryInfo.NormalIndexData= new IntStore(normIndexData);
                }
            }
            return sceneGeometry;
        }
    }
}
